package com.cmc6.request.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cmc6.common.exception.BaseBizCodeEnum;
import com.cmc6.common.mapper.BaseUserInfoMapper;
import com.cmc6.common.model.dto.ChangeBoolByStrId;
import com.cmc6.common.model.entity.EsErrorDO;
import com.cmc6.common.repository.EsErrorRepository;
import com.cmc6.common.util.MyDateUtil;
import com.cmc6.common.util.MyPage;
import com.cmc6.request.mapper.RequestMapper;
import com.cmc6.request.model.dto.RequestAnalysisRequestNumberDTO;
import com.cmc6.request.model.dto.RequestErrorLogPageDTO;
import com.cmc6.request.model.dto.RequestPageDTO;
import com.cmc6.request.model.entity.RequestDO;
import com.cmc6.request.model.vo.RequestAnalysisRequestNumberVO;
import com.cmc6.request.model.vo.RequestPageVO;
import com.cmc6.request.model.vo.RequestRankingVO;
import com.cmc6.request.service.RequestService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class RequestServiceImpl extends ServiceImpl<RequestMapper, RequestDO> implements RequestService {

    @Resource
    BaseUserInfoMapper baseUserInfoMapper;
    @Resource
    ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Resource
    EsErrorRepository esErrorRepository;

    /**
     * 分页排序查询 接口请求记录
     */
    @Override
    public Page<RequestPageVO> myPage(RequestPageDTO dto) {

        return baseMapper.myPage(dto.getPage(), dto);
    }

    /**
     * 统计请求数
     */
    @Override
    public List<RequestAnalysisRequestNumberVO> analysisRequestNumber(RequestAnalysisRequestNumberDTO dto) {

        List<RequestAnalysisRequestNumberVO> resList = new ArrayList<>(); // 本方法返回值

        if (dto.getType() == 1) {
            // 生成【昨天 00:59:59到 现在】的数据
            resList = baseMapper.analysisRequestNumber(MyDateUtil.yesterdayToNowByHour(), "%m-%d %H");
        } else if (dto.getType() == 2) {
            // 生成【上个月全部 到现在】的数据
            resList = baseMapper.analysisRequestNumber(MyDateUtil.lastMonthToNowByDay(), "%Y-%m-%d");
        } else if (dto.getType() == 3) {
            // 生成【上一年 到现在】的数据
            resList = baseMapper.analysisRequestNumber(MyDateUtil.lastYearToNowByMonth(), "%Y-%m");
        } else if (dto.getType() == 4) {
            // 生成【所有（年）】的数据
            resList = baseMapper.analysisRequestNumber(MyDateUtil.allYear(), "%Y");
        }

        return resList;
    }

    /**
     * 请求总数排行
     */
    @Override
    public List<RequestRankingVO> ranking() {
        return baseMapper.ranking();
    }

    /**
     * 慢请求排行
     */
    @Override
    public List<RequestRankingVO> slowRanking() {
        return baseMapper.slowRanking();
    }

    /**
     * 分析所有请求
     */
    @Override
    public RequestRankingVO analyzeAll(RequestPageDTO dto) {
        return baseMapper.analyzeAll(dto);
    }

    /**
     * 分页排序查询 请求错误日志
     */
    @Override
    public Page<EsErrorDO> errorLogPage(RequestErrorLogPageDTO dto) {

        PageRequest pageRequest = PageRequest.of((int)dto.getPageNum() - 1, (int)dto.getPageSize());

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 分页
        queryBuilder.withPageable(pageRequest);
        // 排序
        queryBuilder.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));

        // 搜索
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        Map<String, Object> beanToMap = BeanUtil.beanToMap(dto, false, true);
        Map<String, Field> fieldMap = ReflectUtil.getFieldMap(MyPage.class); // 获取分页的字段
        Set<String> pageFieldSet = fieldMap.keySet();

        for (Map.Entry<String, Object> item : beanToMap.entrySet()) {
            String key = item.getKey();
            if (pageFieldSet.contains(key)) { // 如果是分页字段，则不查询
                continue;
            }
            if ("endTime".equals(key)) {
                continue; // 因为 beginTime，已经处理了，所以不要 endTime
            }
            if ("_id".equals(key)) {
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(Convert.toStr(item.getValue())).defaultField(key));
                continue;
            }
            String value = Convert.toStr(item.getValue());
            if ("beginTime".equals(key)) {
                boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").from(value).to(beanToMap.get("endTime")));
                continue;
            }
            if (item.getValue() instanceof Integer || item.getValue() instanceof Byte || item.getValue() instanceof Long
                || item.getValue() instanceof Boolean) {
                boolQueryBuilder.must(QueryBuilders.queryStringQuery(value).defaultField(item.getKey()));
                continue;
            }
            key = key + ".keyword";
            boolQueryBuilder.must(QueryBuilders.wildcardQuery(key, "*" + value + "*"));
        }
        queryBuilder.withQuery(boolQueryBuilder);

        SearchHits<EsErrorDO> searchHitList = elasticsearchRestTemplate.search(queryBuilder.build(), EsErrorDO.class);

        Page<EsErrorDO> resPage = new Page<>(); // 本方法返回值
        resPage.setRecords(searchHitList.get().map(SearchHit::getContent).collect(Collectors.toList()));
        resPage.setTotal(searchHitList.getTotalHits());
        resPage.setSize(dto.getPageSize());
        resPage.setCurrent(dto.getPageNum());

        return resPage;
    }

    /**
     * 已读/未读 请求错误日志
     */
    @Override
    public String changeErrorLogReadFlag(ChangeBoolByStrId changeBoolByStrId) {

        UpdateQuery updateQuery = UpdateQuery.builder(changeBoolByStrId.getId())
            .withDocument(Document.from(JSONUtil.createObj().set("readFlag", changeBoolByStrId.isFlag()))).build();

        try {
            elasticsearchRestTemplate
                .update(updateQuery, elasticsearchRestTemplate.getIndexCoordinatesFor(EsErrorDO.class));
        } catch (DataAccessResourceFailureException ignored) {
            // 忽略这个异常，还是因为 es的版本问题
        }

        return BaseBizCodeEnum.API_RESULT_OK.getMsg();
    }

}




